iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0

昨天將註冊API完成了。也用 postman 測試過 API可以正確註冊,接著我們今天來試試看吧API連接到APP畫面吧!。來看看APP要如何和後端使用API溝通。

RN串接APP

今天不廢話,直接來上 Cursor。讓我們回到APP的專案。並詢問:

串接註冊API

幫我在註冊頁面加上連接到 API,註冊的router是 http://localhost:3000/auth/register

[!Info] GPT回應
要在註冊頁面中連接到 API 進行註冊,你可以使用 fetch 函數來發送 POST 請求到指定的路由。以下是修改後的 Register.tsx 代碼,已經加入了 API 連接的邏輯:

import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';
import { Alert } from 'react-native';

const Register = ({ navigation }) => {
    const [username, setUsername] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');
    const [error, setError] = useState('');

    const handleRegister = async () => {
        setError('');

        if (!username || !email || !password || !confirmPassword) {
            setError('所有欄位都是必填的');
            return;
        }

        if (password !== confirmPassword) {
            setError('密碼和確認密碼不一致');
            return;
        }

        try {
            const response = await fetch('http://localhost:3000/auth/register', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    username,
                    email,
                    password,
                }),
            });

            const data = await response.json();

            if (response.ok) {
                Alert.alert('註冊成功', '您已成功註冊!', [{ text: '確定', onPress: () => navigation.navigate('Login') }]);
            } else {
                setError(data.message || '註冊失敗,請稍後再試。');
            }
        } catch (error) {
            setError('註冊時發生錯誤,請檢查您的網路連接。');
        }
    };

    return (
        <Box flex={1} justifyContent="center" padding={4}>
            <Text fontSize="2xl" textAlign="center" marginBottom={4}>註冊</Text>
            {error ? <Text color="red.500" textAlign="center" marginBottom={3}>{error}</Text> : null}
            <Input
                placeholder="使用者名字"
                value={username}
                onChangeText={setUsername}
                marginBottom={3}
            />
            <Input
                placeholder="信箱"
                value={email}
                onChangeText={setEmail}
                marginBottom={3}
            />
            <Input
                placeholder="密碼"
                type="password"
                value={password}
                onChangeText={setPassword}
                marginBottom={3}
            />
            <Input
                placeholder="確認密碼"
                type="password"
                value={confirmPassword}
                onChangeText={setConfirmPassword}
                marginBottom={3}
            />
            <Button onPress={handleRegister}>
                註冊
            </Button>
            <Button onPress={() => navigation.navigate('Login')} marginTop={3}>
                返回登入
            </Button>
        </Box>
    );
};

export default Register;

在這段代碼中,我們使用 fetch 函數來發送 POST 請求到 http://localhost:3000/auth/register,並將使用者的註冊資訊作為 JSON 發送。如果註冊成功,則顯示成功的提示,否則顯示錯誤訊息。

我們點選Cusor 的套用,讓程式碼作用。接著到註冊面試試看。會發現出現 Inter Server Error,看起來像伺服器的錯誤。

我們回到API來查看一下是什麼錯誤。原來是我們送出的資料沒有 account。

缺少 Account

看了後端的結果後知道是少了 Account這個欄位。那就把Account接上去吧。

抱歉 註冊的資料欄位是這些 { "username": "your_username", "email": "your_email@example.com", "password": "your_password", "account": "your_account" }

把 postman的測試資料直接貼上去,就算對AI也是要有禮貌的喔。這邊分享一個都市傳說,聽說對AI越有禮貌他回答會越認真越好。

[!Info] GPT回應
要在註冊頁面中更新資料欄位以包含 account,你需要在 Register.tsx 中進行相應的修改。以下是更新後的代碼:

import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';
import { Alert } from 'react-native';

const Register = ({ navigation }) => {
    const [username, setUsername] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');
    const [account, setAccount] = useState('');
    const [error, setError] = useState('');

    const handleRegister = async () => {
        setError('');

        if (!username || !email || !password || !confirmPassword || !account) {
            setError('所有欄位都是必填的');
            return;
        }

        if (password !== confirmPassword) {
            setError('密碼和確認密碼不一致');
            return;
        }

        try {
            const response = await fetch('http://localhost:3000/auth/register', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    username,
                    email,
                    password,
                    account,
                }),
            });

            const data = await response.json();

            if (response.ok) {
                Alert.alert('註冊成功', '您已成功註冊!', [{ text: '確定', onPress: () => navigation.navigate('Login') }]);
            } else {
                setError(data.message || '註冊失敗,請稍後再試。');
            }
        } catch (error) {
            setError('註冊時發生錯誤,請檢查您的網路連接。');
        }
    };

    return (
        <Box flex={1} justifyContent="center" padding={4}>
            <Text fontSize="2xl" textAlign="center" marginBottom={4}>註冊</Text>
            {error ? <Text color="red.500" textAlign="center" marginBottom={3}>{error}</Text> : null}
            <Input
                placeholder="使用者名字"
                value={username}
                onChangeText={setUsername}
                marginBottom={3}
            />
            <Input
                placeholder="信箱"
                value={email}
                onChangeText={setEmail}
                marginBottom={3}
            />
            <Input
                placeholder="密碼"
                type="password"
                value={password}
                onChangeText={setPassword}
                marginBottom={3}
            />
            <Input
                placeholder="確認密碼"
                type="password"
                value={confirmPassword}
                onChangeText={setConfirmPassword}
                marginBottom={3}
            />
            <Input
                placeholder="帳號"
                value={account}
                onChangeText={setAccount}
                marginBottom={3}
            />
            <Button onPress={handleRegister}>
                註冊
            </Button>
            <Button onPress={() => navigation.navigate('Login')} marginTop={3}>
                返回登入
            </Button>
        </Box>
    );
};

export default Register;

在這段代碼中,我們新增了一個 account 的狀態,並在 handleRegister 函數中將其包含在發送到 API 的請求中。同時也在 UI 中新增了一個輸入框來讓使用者填寫帳號。

讓他執行套用後,可以看到多了一個帳號的欄位。雖然他在畫面最下面很不科學啦,但這個之後可以自己手動調整沒關係。

接著來試試看註冊吧,輸入正確資訊後。直接出現 "註冊時發生錯誤,請假查你的網路連接" 這看起來更迷了。也不知是什麼問題。

多了帳號欄位 輸入資料後發生錯誤

一樣看一下後端的終端機,但沒有任何錯誤訊息。再來看看資料庫,咦,確實有多了一筆註冊資料。但這樣老實說也不太知道是什麼問題。不如問問看後端的 Cursor看看。

修正問題

雖然是說應改可能是後端的問題,但我也不太知道問題是什麼。新增註冊的時候後端終端機不會報錯,看資料庫也確實有新增資料。💡不如直接把前端Code丟給後端的Cursor看看吧!?

    try {
        const response = await fetch('http://localhost:3000/auth/register', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                username,
                email,
                password,
                account,
            }),
        });

        const data = await response.json();

        if (response.ok) {
            Alert.alert('註冊成功', '您已成功註冊!', [{ text: '確定', onPress: () => navigation.navigate('Login') }]);
        } else {
            setError(data.message || '註冊失敗,請稍後再試。');
        }
    } catch (error) {
        setError('註冊時發生錯誤,請檢查您的網路連接。');
    }   幫我修改註冊api controll 的回應讓她不會跑到 error

[!Info] GPT回應
要修改註冊 API 控制器的回應,以確保在註冊成功時不會進入錯誤處理邏輯,你可以在 AppControllerregister 方法中,將成功的回應格式化為一個包含訊息的物件。這樣,前端就能夠正確地處理回應。以下是修改後的 AppController 代碼:

import { Controller, Post, Body } from '@nestjs/common';
import { AppService } from './app.service';
import { RegisterDto } from './dto/register.dto';

@Controller('auth')
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Post('register')
  async register(@Body() registerDto: RegisterDto): Promise<{ message: string }> {
    await this.appService.register(registerDto);
    return { message: '註冊成功' }; // 修改這裡,返回一個包含訊息的物件
  }
}

這樣修改後,當註冊成功時,API 將返回一個包含 message 的物件,前端可以根據這個訊息來判斷註冊是否成功,並且不會進入錯誤處理邏輯。

套用看看,再從前端發送註冊訊息。這次就成功了!!!

錯誤原因

雖然我們用GPT幫我們解決了問題,但我們的目標是使用GPT先實作成功再學習,所以解決問題了,要來稍微看一下錯誤是什麼原因造成的。

原本的錯誤是因為在註冊 API 的回應中,當註冊成功時,返回的訊息格式不符合前端的預期。前端的代碼中有一段處理註冊回應的邏輯:

if (response.ok) {
    Alert.alert('註冊成功', '您已成功註冊!', [{ text: '確定', onPress: () => navigation.navigate('Login') }]);
} else {
    setError(data.message || '註冊失敗,請稍後再試。');
}

在這段代碼中,前端期望當註冊成功時,API 會返回一個 response.oktrue 的回應,並且能夠正確解析回應的內容。如果後端返回的內容不符合預期(例如,沒有返回 message 屬性),前端就會進入 else 區塊,並顯示錯誤訊息。

原本的後端代碼在註冊成功時只返回了一個字串 'User registered successfully',這樣的回應格式不符合前端的要求,導致前端無法正確處理成功的情況,進而進入錯誤處理邏輯。

透過修改後端的回應格式,讓它返回一個包含 message 屬性的物件,前端就能夠正確識別註冊是否成功,並顯示相應的提示。

結語

我們今天一樣使用Cursor這個編輯器使用 GPT幫我們完成APP串接API連接到 BE的實作。

但我們在這之中遇到了一些問題,我們透過和GPT溝通解決問題。最後遇到一個雖然BE註冊成功,也有資料但是前端一樣顯示錯誤。

這其實是一個滿常見的前後端溝通不一致的情況。這也是我們一開始設計的時候沒有說的很仔細,和GPT的說明也不夠清楚造成的。

AI工具雖然好用,但是還是需要使用者明白知道要做什麼知道自己在做什麼,才能下明確的指令讓AI幫你完成工作。

工商時間

又到了我們最後的工商時間啦~~~。Tom有在經營 Pocast,目前比較偏向職場類的閒聊,未來Podcast預計更新與程式軟體開發相關的主題。有興趣的朋友follow一下。

可以透過以下平台收聽: Apple podcast Spotify KKBOX
Linking Tree

#it鐵人


上一篇
[Day14] 註冊會員API
下一篇
[DAY16] 使用 Redux 狀態管理
系列文
30天 使用chatGPT輔助學習APP完成接案任務委託30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言